#pragma once

#include <iostream>
#include <string>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"

enum
{
    SOCKERROR = 1,
    BINDERROR,
    LISTENERROR,
    USAGEERROR
};

class TcpServer;
class ThreadData
{
public:
    ThreadData(int sock, const std::string& ip, const uint16_t& port, TcpServer* t)
    :m_sockfd(sock), m_clientip(ip), m_clientport(port), m_tsvr(t)
    {}
public:
    int m_sockfd;
    std::string m_clientip;
    uint16_t m_clientport;
    TcpServer* m_tsvr;
};

class TcpServer
{
    static uint16_t defaultport;
    static std::string defaultip;
    static const int g_back;

public:
    TcpServer(uint16_t port = defaultport, const std::string &ip = defaultip) : _port(port), _ip(ip), _listen_sockfd(-1)
    {
    }

    void init()
    {
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            LOG(FATAL, "create sockfd failed");
            exit(SOCKERROR);
        }
        LOG(INFO, "create sockfd success");
        int opt = 1;
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));  // 防止偶发性的服务器无法立即重启

        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_listen_sockfd, (struct sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            LOG(FATAL, "bind listen_sock failed");
            exit(BINDERROR);
        }
        LOG(INFO, "bind listen_sock success");

        n = listen(_listen_sockfd, g_back);
        if (n < 0)
        {
            LOG(FATAL, "listen listen_sock failed");
            exit(LISTENERROR);
        }
        LOG(INFO, "listen listen_sock success");
    }

    void start()
    {
        // 有可能写端也会出现问题。类似管道那里，客户端发送完消息有可能立马关闭。因此要处理
        signal(SIGPIPE, SIG_IGN);
        ThreadPool<Task>::GetInstance()->Start();
        // for(;;) fork();  // 对accept加锁处理
        // signal(SIGCHLD, SIG_IGN);
        while (1)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int server_sock = accept(_listen_sockfd, (struct sockaddr *)&client, &len);
            if (server_sock < 0)
            {
                LOG(WARNIG, "no link to server, waiting for ....");
                continue;
            }
            LOG(INFO, "get a new link ...");
            // const char *clientip = inet_ntoa(client.sin_addr);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
            // std::cout << clientip << std::endl;
            uint16_t clientport = ntohs(client.sin_port);
            // std::cout << clientport << std::endl;

            // version1 -- 单进程版
            // service(server_sock, clientip, clientport);
            // close(server_sock);

            // version2 -- 多进程版
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // child
            //     close(_listen_sockfd);
            //     if(fork() > 0) exit(0);  // 这样写，wait等待就不会阻塞了。因为这里如果fork大于0，就是子进程自己，进而退出。提供服务的就是孙子进程了。
            //     service(server_sock, clientip, clientport); // 孙子进程，被系统领养。一旦孙子进程执行完，系统会回收资源。
            //     close(server_sock);
            //     exit(0);
            // }
            // // father
            // close(server_sock);
            // // 子进程创建好后，立马退出。waitpid立马返回。孙子进程提供服务的时候，waitpid已经获取了僵尸，返回后继续accept了。
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;

            // version3 -- 多线程版。不想提供长服务，不想来一个客户端就创建一个。这里连接都建立好了，才开始创建线程。效率太低
            // ThreadData* td = new ThreadData(server_sock, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routinue, td);
            // // pthread_join(tid, nullptr);  // 主线程会阻塞。

            // version4 -- 线程池。提前提供好
            Task t(server_sock, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    // static void* Routinue(void* args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData* td = static_cast<ThreadData*>(args);
    //     td->m_tsvr->service(td->m_sockfd, td->m_clientip, td->m_clientport);
    //     delete td;
    //     return nullptr;
    // }

    // void service(int fd, const std::string &clientip, const uint16_t &clientport)
    // {
    //     char buffer[256];
    //     while (true)
    //     {
    //         int n = read(fd, buffer, sizeof(buffer));
    //         if (n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << buffer << std::endl;
    //             std::string recvdata = sendMessage(clientip, clientport) + buffer;

    //             write(fd, recvdata.c_str(), recvdata.size());
    //         }
    //         else if(0 == n)  // 如果客户端退出，read会读取到0
    //         {
    //             LOG(INFO, "client quit ...");
    //             break; 
    //         }
    //         else{
    //             LOG(WARING, "read error ....");
    //             break;
    //         }
    //     }
    // }

    std::string sendMessage(const std::string &clientip, const uint16_t &clientport)
    {
        std::string recvdata;
        recvdata += "[";
        recvdata += clientip;
        recvdata += ":";
        recvdata += std::to_string(clientport);
        recvdata += "]";
        return recvdata;
    }

    ~TcpServer() {}

private:
    int _listen_sockfd;
    std::string _ip;
    uint16_t _port;
};

uint16_t TcpServer::defaultport = 8080;
std::string TcpServer::defaultip = "0.0.0.0";
const int TcpServer::g_back = 10;